package org.jmc.refmanager.domain.model;

import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

/**
 * Entity implementation class for Entity: Change
 * 
 */
@Entity
@Table(name = "changes")
@NamedQueries({
	// -------------------------------------------------------------------------------
	// My Group Tasks
	// -------------------------------------------------------------------------------
	@NamedQuery(name = Change.FIND_MY_GROUP_TASKS_IN_STATUS_FOR_OP, 
			query = "SELECT c FROM Change c, User u " +
					" WHERE c.directionId = u.validationScope.id AND u.matricule=?1 " +
					" AND c.status IN (?2) AND c.operation IN (?3)"),
	@NamedQuery(name = Change.FIND_GROUP_TASKS, 
			query = "SELECT c FROM Change c " +
					" WHERE c.directionId = ?1 AND c.status IN (?2)"),
	@NamedQuery(name = Change.COUNT_MY_GROUP_TASKS_IN_STATUS_FOR_OP, 
			query = "SELECT count(c) FROM Change c, User u " +
					" WHERE c.directionId = u.validationScope.id AND u.matricule=?1 " +
					" AND c.status IN (?2) AND c.operation IN (?3)"),
//	@NamedQuery(name = Change.COUNT_MY_GROUP_TASKS, 
//			query = "SELECT count(c) FROM Change c, User u " +
//					" WHERE c.directionId = u.validationScope.id AND u.matricule=?1 AND c.operation IN (?2)"),
	// -------------------------------------------------------------------------------
	// My Tasks
	// -------------------------------------------------------------------------------
	@NamedQuery(name = Change.FIND_MY_TASKS, 
			query = "SELECT c FROM Change c " +
					" WHERE c.lockedByUser.matricule=?1"),
	@NamedQuery(name = Change.COUNT_MY_TASKS, 
			query = "SELECT count(c) FROM Change c " +
					" WHERE c.lockedByUser.matricule=?1")
})
public class Change implements Serializable {
	private static final long serialVersionUID = 1L;

	public static final String FIND_MY_GROUP_TASKS_IN_STATUS_FOR_OP = "Change.findMyGroupTasksInStatusForOperation";
	public static final String COUNT_MY_GROUP_TASKS_IN_STATUS_FOR_OP = "Change.CountMyGroupTasksInStatusForOperation";
	public static final String FIND_MY_TASKS = "Change.findMyTasks";
	public static final String COUNT_MY_TASKS = "Change.CountMyTasks";
	public static final String FIND_GROUP_TASKS = "Change.FindGroupTasks";
	
	/**
	 * Status d'une teche.
	 * Au depart une teche est e l'etat AVAILABLE.
	 * Si un approbateur prend la teche pour la valider,
	 * le statut passe e RESERVED.
	 * Il peut decider de la relecher pour ne pas la traiter,
	 * la teche repasse au statut AVAILABLE.
	 * 
	 * Si la teche est reservee, suivant l'action de l'utilisateur,
	 * elle passe e l'etat APPROVED ou REJECTED. 
	 *
	 */
	public enum TaskStatus {
		APPROVED("Approuvé"), REJECTED("Rejeté"), AVAILABLE("Disponible"), RESERVED("Réservé");

		private String label;
		
		/**
		 * @param name
		 * @param ordinal
		 */
		TaskStatus(String label) {
			this.setLabel(label);
		}

		private void setLabel(String label) {
			this.label = label;
		}

		public String getLabel() {
			return label;
		}
	}

	public enum Operation {
		CREATE, DELETE, MOVE, UPDATE, MOVE_UPDATE
	}

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "changeId")
	private Integer id;
	
	@Column(name="name", nullable=false, updatable=false)
	private String name;
	
	@Column(name="operation", nullable=false, updatable=false)
	@Enumerated(EnumType.STRING)
	private Operation operation;

	@Column(name="status", nullable=false)
	@Enumerated(EnumType.STRING)
	private TaskStatus status;

	@Column(name="approverName")
	private String approverName;
	
	@Column(name="description", nullable=false, updatable=false)
	private String objectDescription;
	
	@Lob
	private String object;
	
	@ManyToMany(mappedBy="myTasks")
	private Set<User> approvers = new HashSet<User>(0);
	
	@OneToOne(optional=true, fetch=FetchType.LAZY)
	@JoinColumn(name = "lockedByUserId")
	private User lockedByUser;

	@JoinColumn(name="authorName", updatable=false, nullable=false)
	private String authorName;

	@Column(name = "directionId", updatable=false)
	private Integer directionId;

	/*
	 * Date de creation
	 */
	@Temporal(TemporalType.DATE)
	@Column(name="creationDate", updatable = false)
	private Date creationDate;

	/*
	 * Date de derniere modification
	 */
	@Version
	@Column(name="lastModDateTime")
	private Date modificationDateTime;

	
	// -------------------------------------------------
	// Constructeur
	// -------------------------------------------------
	public Change() {
		super();
	}

	// -------------------------------------------------
	// CYCLE DE VIE JPA
	// -------------------------------------------------
	@PrePersist
	protected void prePersit() {
		this.setCreationDate(new Date());
	}

	// -------------------------------------------------
	// Getters and Setters
	// -------------------------------------------------
	public Integer getId() {
		return this.id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Operation getOperation() {
		return this.operation;
	}

	public void setOperation(Operation operation) {
		this.operation = operation;
	}

	/**
	 * @return the object
	 */
	public String getObject() {
		return object;
	}

	/**
	 * @param object the object to set
	 */
	public void setObject(String object) {
		this.object = object;
	}

	/**
	 * @return the status
	 */
	public TaskStatus getStatus() {
		return status;
	}

	/**
	 * @param status the status to set
	 */
	public void setStatus(TaskStatus status) {
		this.status = status;
	}

	/**
	 * @param lockByUser the lockByUser to set
	 */
	public void setLockedByUser(User lockedByUser) {
		this.lockedByUser = lockedByUser;
	}

	/**
	 * @return the lockByUser
	 */
	public User getLockedByUser() {
		return lockedByUser;
	}

	/**
	 * @param approvers the approvers to set
	 */
	public void setApprovers(Set<User> approvers) {
		this.approvers = approvers;
	}

	/**
	 * @return the approvers
	 */
	public Set<User> getApprovers() {
		return approvers;
	}

	/**
	 * @return the directionId
	 */
	public Integer getDirectionId() {
		return directionId;
	}

	/**
	 * @param directionId the directionId to set
	 */
	public void setDirectionId(Integer directionId) {
		this.directionId = directionId;
	}

	/**
	 * @return the creationDate
	 */
	public Date getCreationDate() {
		return creationDate;
	}

	/**
	 * @param creationDate the creationDate to set
	 */
	public void setCreationDate(Date creationDate) {
		this.creationDate = creationDate;
	}

	/**
	 * @return the modificationDateTime
	 */
	public Date getModificationDateTime() {
		return modificationDateTime;
	}

	/**
	 * @param modificationDateTime the modificationDateTime to set
	 */
	public void setModificationDateTime(Date modificationDateTime) {
		this.modificationDateTime = modificationDateTime;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String
				.format("Change [id=%s, name=%s, operation=%s, status=%s, lockedByUser=%s, directionId=%s, creationDate=%s, modificationDateTime=%s]",
						id, name, operation, status, lockedByUser, directionId,
						creationDate, modificationDateTime);
	}

	/**
	 * @return the authorName
	 */
	public String getAuthorName() {
		return authorName;
	}

	/**
	 * @param authorName the authorName to set
	 */
	public void setAuthorName(String authorName) {
		this.authorName = authorName;
	}

	/**
	 * @return the approverName
	 */
	public String getApproverName() {
		return approverName;
	}

	/**
	 * @param approverName the approverName to set
	 */
	public void setApproverName(String approverName) {
		this.approverName = approverName;
	}

	/**
	 * @return the objectDescription
	 */
	public String getObjectDescription() {
		return objectDescription;
	}

	/**
	 * @param objectDescription the objectDescription to set
	 */
	public void setObjectDescription(String objectDescription) {
		this.objectDescription = objectDescription;
	}

}
